package zdream.pmfield.stage.buff.foe;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import zdream.pmfield.data.foe.BStat;
import zdream.pmfield.data.foe.VStat;
import zdream.pmfield.stage.Stage;
import zdream.pmfield.stage.action.DazeAction;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.director.Roll;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.TimelineOrderChangeMessage;
import zdream.pmfield.stage.object.FoeActor;
import zdream.pmfield.stage.object.FoeActor.BStatLevelUpdateItem;
import zdream.utils.StringUtils;

/**
 * <p>处理如下事物:
 * <li>确保 {@link FoeActor#vStats} 的数值在 5 到 95 之间
 * <li>记录 {@link FoeActor} 的能力变化等级, 确保等级在 -3 到 3 之间
 * <li>一般能力提升和下降只持续一段时间 (一般 3 回合). 也就是默认三回合后,
 * 能力会向 0 级回调一级.
 * </li>
 * </p>
 *
 * @author Zdream
 * @date 2023-12-11
 * @since 0.0.1
 */
public class FoeOriginalBuff extends FoeStageBuff {
	public static final String CATEGORY = FoeActor.ORIGINAL_BUFF_CATEGORY;
	public static final String TITLE_BSTAT_LEVEL_RESTORE = "b-stat-level-restore";
	public final Stage stage;

	public FoeOriginalBuff(FoeActor foe) {
		super(foe);
		this.stage = foe.stage;

		this.lastSpeed = foe.bStats[BStat.SP.ordinal()];
	}

	@Override
	public void init() {
		onHook(HookType.ON_FOE_REFRESH_VSTAT)
				.handle(this::onFoeRefreshVStat)
				.buildAndSave();
		onHook(HookType.ON_FOE_REFRESH_BSTAT)
				.handle(this::refreshBStat)
				.buildAndSave();
		onHook(HookType.BEFORE_FOE_TURN_START)
				.handle(this::beforeTurnStart)
				.withPriority(9000) // the last
				.buildAndSave();
		onHook(HookType.BEFORE_FOE_TURN_START)
				.handle(this::checkRestoreBStatLevel)
				.buildAndSave();
	}

	/* **********
	 *  VStat   *
	 ********** */

	public void onFoeRefreshVStat(Hooks.HookContext rawContext) {
		Hooks.FoeBuffContext context = rawContext.as();
		if (context.foe != target) {
			return;
		}

		final short[] vStats = target.vStats;
		for (int i = 0; i < vStats.length; i++) {
			if (vStats[i] > 95) {
				vStats[i] = 95;
			} else if (vStats[i] < 5) {
				vStats[i] = 5;
			}
		}
	}

	/* **********
	 *  BStat   *
	 ********** */

	public final byte[] bStatLevels = new byte[BStat.values().length];
	public final byte[] bStatOriginLevels = new byte[BStat.values().length];
	public final float[] bStatUpdatedTime = new float[BStat.values().length];
	public final byte BSTAT_MAX_LEVEL = 3, BSTAT_MIN_LEVEL = -3;

	/**
	 * 上一次用于计算 {@link FoeActor#speedCost} 时使用的速度值.
	 */
	public short lastSpeed;

	public boolean hasAnyBStatLevelUpdated() {
		for (int i = 1; i < bStatLevels.length; i++) {
			if (bStatLevels[i] != 0) {
				return true;
			}
		}
		return false;
	}

	public int getBStatLevel(BStat bStat) {
		return bStatLevels[bStat.ordinal()];
	}

	public void updateBStatLevel(FoeActor.BStatLevelUpdateReport report) {
		for (BStatLevelUpdateItem item : report.items.values()) {
			if (item.bStat == BStat.HP) {
				throw new IllegalArgumentException("HP 的等级不能升降");
			}

			int index = item.bStat.ordinal();
			final byte currentLevel = bStatLevels[index];
			if (!item.accept || item.change && item.level == 0) {
				item.setLevelChange(currentLevel, currentLevel);
				continue;
			}

			if (!item.change) {
				bStatLevels[index] = (byte) Math.max(BSTAT_MIN_LEVEL, Math.min(BSTAT_MAX_LEVEL, item.level));
				item.setLevelChange(currentLevel, bStatLevels[index]);
				bStatUpdatedTime[index] = target.stage.now();
				continue;
			}

			if (currentLevel == BSTAT_MAX_LEVEL && item.level > 0) {
				item.reject(this);
				item.setLevelChange(currentLevel, currentLevel);
				continue;
			} else if (currentLevel == BSTAT_MIN_LEVEL && item.level < 0) {
				item.reject(this);
				item.setLevelChange(currentLevel, currentLevel);
				continue;
			}

			int targetLevel = currentLevel + item.level;
			bStatLevels[index] = (byte) Math.max(BSTAT_MIN_LEVEL, Math.min(BSTAT_MAX_LEVEL, targetLevel));
			bStatUpdatedTime[index] = target.stage.now();
			item.setLevelChange(currentLevel, bStatLevels[index]);
		}
	}

	private void refreshBStat(Hooks.HookContext rawContext) {
		Hooks.RefreshFoeBStatContext context = rawContext.as();

		for (int i = 1; i < bStatLevels.length; i++) {
			byte level = bStatLevels[i];
			float rate = fixRateForBStatLevel(level);

			short targetValue = (short) (target.bStats[i] * context.fixed[i] * rate);
			if (targetValue < 1) {
				targetValue = 1;
			}
			target.bStats[i] = targetValue;
		}

		// update speed
		short currentSpeed = target.bStats[BStat.SP.ordinal()];
		if (currentSpeed == lastSpeed) {
			return;
		}

		this.lastSpeed = currentSpeed;
		float lastCost = target.speedCost;
		target.speedCost = stage.timeline.calculateSpeedCost(currentSpeed);
		float remainFrom = target.speedRemains;
		target.speedRemains = target.speedRemains / lastCost * target.speedCost;

		pushMessage(new TimelineOrderChangeMessage(target)
				.withSpeedRemainChanged(remainFrom, target.speedRemains));
	}

	private void refreshSpeed(Hooks.HookContext rawContext) {
		short currentSpeed = target.bStats[BStat.SP.ordinal()];
		if (currentSpeed == lastSpeed) {
			return;
		}

		this.lastSpeed = currentSpeed;
		float lastCost = target.speedCost;
		target.speedCost = stage.timeline.calculateSpeedCost(currentSpeed);
		target.speedRemains = target.speedRemains / lastCost * target.speedCost;
	}

	private float fixRateForBStatLevel(byte level) {
		if (level == 0) {
			return 1.0f;
		}

		if (level > 0) {
			return (level + 3) / 3.0f;
		} else {
			return 3.0f / (3 - level);
		}
	}

	private void checkRestoreBStatLevel(Hooks.HookContext rawContext) {
		Hooks.BeforeTurnHookContext context = rawContext.as();
		if (context.foe != target) {
			return;
		}

		float now = target.stage.now();
		final FoeActor.BStatLevelUpdateReport report = target.updateBStatLevel();
		boolean anyChanged = false;

		for (int i = 1; i < bStatUpdatedTime.length; i++) {
			byte currentLevel = bStatLevels[i];
			if (currentLevel == bStatOriginLevels[i]) {
				continue;
			}

			if (now > bStatUpdatedTime[i] + 3) {
				// 能力等级向 bStatOriginLevels 改变 1 级
				report.withBStatSet(BStat.values()[i],
						currentLevel > bStatOriginLevels[i] ? currentLevel - 1 : currentLevel + 1);
				anyChanged = true;
			}
		}

		if (anyChanged) {
			report.withTitle(TITLE_BSTAT_LEVEL_RESTORE).commit();
		}
	}

	/* **********
	 *  Action  *
	 ********** */

	private void beforeTurnStart(Hooks.HookContext rawContext) {
		Hooks.BeforeTurnHookContext context = rawContext.as();
		if (context.foe != target) {
			return;
		}

		if (!context.allowAct) {
			target.putActionNow(new DazeAction(target));
			return;
		}

		// 判定敏捷骰子
		Roll[] rolls = new Roll[context.movePointRollCount];
		for (int i = 0; i < rolls.length; i++) {
			rolls[i] = target.createRoll(VStat.FLEXIBILITY);
		}
		rolls[0].useFocus(); // 免费的

		Hooks.RollCheckHookContext rollHooks = target.triggerHook(HookType.DURING_ROLL_CHECK).as();
		rollHooks.withRolls(rolls).withFoe(target).withTitle(FoeActor.TITLE_MOVE_POINT).run();

		pushMessage(new RollReportMessage(target, rolls).withTitle(FoeActor.TITLE_MOVE_POINT));
		int successCount = (int) Arrays.stream(rolls)
				.filter(roll -> roll.result)
				.count();

		target.restoreActionPoint(1, successCount);
	}

	/* **********
	 *  Others  *
	 ********** */

	@Override
	public String getCategory() {
		return CATEGORY;
	}

	@Override
	public String toString() {
		if (hasAnyBStatLevelUpdated()) {
			List<String> strings = new ArrayList<>();
			for (int i = 1; i < bStatLevels.length; i++) {
				byte level = bStatLevels[i];
				if (level == 0) {
					continue;
				}
				strings.add(String.format("%s%s%d", BStat.values()[i], level >= 0 ? "+" : "", level));
			}
			return StringUtils.join(", ", strings);
		}

		return "我是 Foe";
	}
}
